﻿using System;
using System.Collections;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web.Script.Serialization;
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;

namespace Pdogs.ApplicationPageService.Layouts.Pdogs.ApplicationPageService
{
	/* Use:
		var request = _spPageContextInfo.webServerRelativeUrl
							+ '/_layouts/Pdogs.ApplicationPageService/MyAppService.aspx';
		$.post(request,
				{ op: 'GetDateTime', format: 'dd.MM.yyyy hh:mm:ss' })
			.done(function (data) {
				$('#time').append(data.d.results + "<br/>");
			})
			.fail(function (error) {
			});

	 */
	public partial class MyAppService : LayoutsPageBase
	{
		private const string OpNameParameter = "op";

		private const string JsonErrorFmt =
			@"{{
  ""error"" : {{
    ""code"" : ""{0}"",
    ""message"" : {{
      ""lang"" : ""{4}"",
      ""value"" : ""Method {1}: {2}{3}""
    }}
  }}
}}";

		// ReSharper disable CoVariantArrayConversion

		/// <summary>
		///    Handles the Load event of the Page control.
		/// </summary>
		/// <param name="sender">The source of the event.</param>
		/// <param name="e">
		///    The <see cref="System.EventArgs" /> instance containing the event data.
		/// </param>
		protected void Page_Load(object sender, EventArgs e)
		{
			var opName = "Unknown";
			try
			{
				opName = Request.Params[OpNameParameter];
				if (string.IsNullOrEmpty(opName))
					throw new Exception(string.Format("{0} parameter not found in request!", OpNameParameter));

				var opMethodInfo = GetType().GetMethod(opName);
				if (opMethodInfo == null) 
					throw new Exception("Operation not found!");

				var parameters = opMethodInfo.GetParameters()
										.Select(pn => Convert.ChangeType(Request.Params[pn.Name], pn.ParameterType))
										.ToArray();
				var opResult = parameters.Length > 0
											? opMethodInfo.Invoke(this, parameters)
											: opMethodInfo.Invoke(this, null);
				WriteResponse(200, opResult);
			}
			catch (Exception ex)
			{
				var exResult = string.Format(JsonErrorFmt, 
														400, 
														opName,
														ex.Message,
														ex.InnerException != null
															? string.Format(" &raquo; {0}", ex.InnerException.Message)
															: string.Empty,
														Web.Locale.Name);
				WriteResponse(400, exResult);
			}
			Response.End();
		}

		// ReSharper restore CoVariantArrayConversion

		/// <summary>
		///    Writes the response.
		/// </summary>
		/// <param name="httpStatusCode">The HTTP status code.</param>
		/// <param name="opResult">The operation result.</param>
		private void WriteResponse(int httpStatusCode, object opResult)
		{
			Response.Clear();
			Response.StatusCode = httpStatusCode;
			Response.ContentType = "application/json; charset=utf-8";
			Response.Write(opResult);
		}

		/// <summary>
		///    Creates the json response.
		/// </summary>
		/// <param name="data">The data.</param>
		/// <returns></returns>
		private static string CreateJsonResponse(object data)
		{
			var js = new JavaScriptSerializer();
			string results;
			if (data is IList)
			{
				var list = (data as IList);
				var enumerable = list as object[] ?? list.Cast<object>().ToArray();
				var count = enumerable.Count();
				results = js.Serialize(new
					{
						d = new
							{
								results = enumerable,
								__count = count
							}
					});
			}
			else
			{
				results = js.Serialize(new
					{
						d = new
							{
								results = data
							}
					});
			}
			return results;
		}

		#region OP::GetDateTime

		/// <summary>
		///    Gets the time.
		/// </summary>
		/// <param name="format">The format.</param>
		/// <returns></returns>
		public string GetDateTime(string format)
		{
			if (string.IsNullOrEmpty(format)) throw new ArgumentNullException("format");
			return CreateJsonResponse(DateTime.Now.ToString(format));
		}

		#endregion

		#region OP::GetSiteAccessGroups

		// ReSharper disable NotAccessedField.Local
		#region internal group & user serialization classes

		private class JsonGroup
		{
			public int Id;

			public string FullName;
			public string DisplayName;
			public string Description;
			public JsonUser[] Users;
		}

		private class JsonUser
		{
			public int Id;
			public string DisplayName;
			public string LoginName;
			public string Email;
			public bool IsSiteAdmin;
		}

		#endregion

		/// <summary>
		/// Gets the site access groups.
		/// </summary>
		/// <returns></returns>
		public string GetSiteAccessGroups(bool includeUsers)
		{
			var re = new Regex(@"([^_ ]+)$");
			var associatedGroups = new[]
				{
					Web.AssociatedOwnerGroup,
					Web.AssociatedMemberGroup, 
					Web.AssociatedVisitorGroup
				};
			var data = 
				associatedGroups
					.Where(g => g != null)
					.Select(g => new JsonGroup
						{
							Id = g.ID,
							FullName = g.Name,
							DisplayName = re.Match(g.Name).Groups[1].Value,
							Description = g.Description,
							Users = includeUsers
										? g.Users.Cast<SPUser>()
											.Select(u=> new JsonUser
															{
																Id = u.ID, 
																DisplayName = u.Name, 
																LoginName = u.LoginName, 
																Email = u.Email, 
																IsSiteAdmin = u.IsSiteAdmin
															})
											.ToArray()
										: null
						}).ToArray();
			return CreateJsonResponse(data);
		}

		#endregion
	}
}
